#include <ut/UnitTest.h>
#include <illib/Log.h>
#include <illib/Vector.h>
#include <illib/System.h>
#include <illib/Exception.h>
#include <sstream>
#include <list>
#include <string.h>

using namespace illib;
using namespace std;

namespace illib_ut{
	template<class T> void testVectorAgainstExpected(Buffer::Vector<T> & list, const char* indexes) {
		size_t length = strlen(indexes);
		char * reversed = new char[length+1];
		reversed[length] = '\0';
		for(size_t i = 0; i < length; i++) reversed[i] = indexes[(length-1)-i];
		//Console::print("Testing forwards %s\n", indexes);
		//Console::print("Testing reverse %s\n", reversed);
		{
			stringstream stream(indexes);
			size_t number;
			size_t i = 0;
			T head = 0;
			T tail = 0;
			while (!stream.eof()){
				stream >> number;
				T number = *list.get(i);
				ut::test(AT(), EX(*list.get(i) == number), TN(LinkedList::List::get));
				if(i == 0) head = number;
				tail = number;
				i++;
			}
		}
		{
			stringstream stream(indexes);
			size_t number;
			Buffer::Iterator<T> iterator(list);
			while(iterator.current()){
				stream >> number;
				//Console::print("%d %d %d\n",*iterator.current(), number, iterator.index());
				ut::test(AT(), EX(*iterator.current() == number), TN(Buffer::Iterator::current));
				iterator.next();
			}
		}
		{
			stringstream stream(reversed);
			size_t number;
			Buffer::Iterator<T> iterator(list, true);
			while(iterator.current()){
				stream >> number;
				//Console::print("%d %d %d\n",*iterator.current(), number, iterator.index());
				ut::test(AT(), EX(*iterator.current() == number), TN(Buffer::Iterator::current));
				iterator.prev();
			}
		}
		{
			stringstream stream(indexes);
			size_t number;
			Buffer::Iterator<T> iterator(list);
			while (!stream.eof()){
				stream >> number;
				//Log::debug("Expected %p, got %p\n", &elements[index], iterator.current());
				ut::test(AT(), EX(*iterator.current() == number),TN(Buffer::Iterator::current));
				iterator.next();
			}
		}
		{

			Buffer::Iterator<T> iterator(list, true);
			size_t number;
			stringstream stream(reversed);
			while (!stream.eof()){
				stream >> number;
				//Log::debug("Expected %p, got %p\n", &elements[index], iterator.current());
				ut::test(AT(), EX(*iterator.current() == number),TN(Buffer::Iterator::current));
				iterator.prev();
			}
		}
		delete reversed;
	}

	template<class T> void testVector(Buffer::Vector<T> &vector){
		T * n = 0;

		ut::test(AT(), EX(!vector.length()),TN(Vector::length));
		ut::test(AT(), EX(!vector.remove(0,1)),TN(Vector::remove));

		ut::test(AT(), EX(!vector.add(1)),TN(Vector::add));
		ut::test(AT(), EX(!vector.get(0)),TN(Vector::get));
		ut::test(AT(), EX(!vector.get(1)),TN(Vector::get));

		n = vector.add(0, 1);
		ut::test(AT(), EX(n),TN(Vector::add));
		*n = 0;
		ut::test(AT(), EX(vector.length() == 1),TN(Vector::length));
		testVectorAgainstExpected<T>(vector, "0");

		n = vector.add(0, 1);
		ut::test(AT(), EX(n),TN(Vector::add));
		*n = 1;
		ut::test(AT(), EX(vector.length() == 2),TN(Vector::length));
		testVectorAgainstExpected<T>(vector, "1 0");

		n = vector.add(1, 1);
		ut::test(AT(), EX(n),TN(Vector::add));
		ut::test(AT(), EX(vector.length() == 3),TN(Vector::length));
		*n = 2;
		testVectorAgainstExpected<T>(vector, "1 2 0");

		ut::test(AT(), EX(!vector.add(4,1)),TN(Vector::add));
		n = vector.add(3,1);
		ut::test(AT(), EX(n),TN(Vector::add));
		ut::test(AT(), EX(vector.length() == 4),TN(Vector::length));
		*n = 3;
		testVectorAgainstExpected<T>(vector, "1 2 0 3");

		n = vector.add(2, 1);
		ut::test(AT(), EX(n),TN(Vector::add));
		*n = 5;
		testVectorAgainstExpected<T>(vector, "1 2 5 0 3");

		n = vector.add(0, 1);
		ut::test(AT(), EX(n),TN(Vector::add));
		*n = 6;
		testVectorAgainstExpected<T>(vector, "6 1 2 5 0 3");

		n = vector.add(vector.length(), 1);
		ut::test(AT(), EX(n),TN(Vector::add));
		*n = 7;
		testVectorAgainstExpected<T>(vector, "6 1 2 5 0 3 7");

		n = vector.add(6, 1);
		ut::test(AT(), EX(n),TN(Vector::add));
		*n = 8;
		testVectorAgainstExpected<T>(vector, "6 1 2 5 0 3 8 7");

		ut::test(AT(), EX(vector.remove(0, 1)), TN(Vector::remove));
		testVectorAgainstExpected<T>(vector, "1 2 5 0 3 8 7");

		ut::test(AT(), EX(!vector.remove(7, 1)), TN(Vector::remove));
		ut::test(AT(), EX(vector.remove(6, 1)), TN(Vector::remove));
		testVectorAgainstExpected<T>(vector, "1 2 5 0 3 8");

		ut::test(AT(), EX(vector.remove(3, 1)), TN(Vector::remove));
		testVectorAgainstExpected<T>(vector, "1 2 5 3 8");
		ut::test(AT(), EX(vector.length() == 5),TN(Vector::length));

		n = vector.add(vector.length(), 1);
		ut::test(AT(), EX(n),TN(Vector::add));
		*n = 0;
		testVectorAgainstExpected<T>(vector, "1 2 5 3 8 0");

		n = vector.add(0, 1);
		ut::test(AT(), EX(n),TN(Vector::add));
		*n = 4;
		testVectorAgainstExpected<T>(vector, "4 1 2 5 3 8 0");

		ut::test(AT(), EX(vector.remove(0, 1)), TN(Vector::remove));
		testVectorAgainstExpected<T>(vector, "1 2 5 3 8 0");

		ut::test(AT(), EX(vector.remove(vector.length()-1, 1)), TN(Vector::remove));
		testVectorAgainstExpected<T>(vector, "1 2 5 3 8");

		n = vector.add(3, 3);
		ut::test(AT(), EX(n),TN(Vector::add));
		*n = 7;
		n++;
		*n = 4;
		n++;
		*n = 6;
		testVectorAgainstExpected<T>(vector, "1 2 5 7 4 6 3 8");

		ut::test(AT(), EX(vector.remove(2, 4)), TN(Vector::remove));
		testVectorAgainstExpected<T>(vector, "1 2 3 8");
	}

	void Vector_ut(){
		ut::UnitTestLogger logger(TN(ut::illib::Vector_ut));
		BlockAllocator::BasicAllocator allocator(1, 0);
		{
			Buffer::Vector<uint8_t> vector(allocator,0);
			testVector<uint8_t>(vector);
			vector.clear();
			testVector<uint8_t>(vector);
			vector.destroy();
		}
			Buffer::Vector<uint64_t> vector(allocator,0);
			testVector<uint64_t>(vector);
			vector.clear();
			testVector<uint64_t>(vector);
			vector.destroy();
	}
};

